using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace Rage
{
    #region Item
    public class Item : IComparable<Item>
    {
        [XmlElement("ItemLevel")]
        public int _itemLevel;

        [XmlElement("Rating")]
        public int _rating;

        [XmlElement("DisplayId")]
        public string _displayId;

        [XmlElement("DisplaySlot")]
        public int _displaySlot;

        [XmlElement("IconPath")]
        public string _iconPath;

        [XmlElement("Stats")]
        public Stats _stats = new Stats();

        [XmlElement("Quality")]
        public ItemQuality _quality;

        [DefaultValueAttribute("")]
        [XmlElement("SetName")]
        public string _setName;
        
        [XmlElement("Type")]
        public ItemType _type = ItemType.None;

        [XmlElement("Faction")]
        public ItemFaction _faction = ItemFaction.Neutral;
        
        [DefaultValueAttribute(0)]
        [XmlElement("MinDamage")]
        public int _minDamage = 0;

        [DefaultValueAttribute(0)]
        [XmlElement("MaxDamage")]
        public int _maxDamage = 0;

        [DefaultValueAttribute(0)]
        [XmlElement("DamageType")]
        public ItemDamageType _damageType = ItemDamageType.Kinetic;

        [DefaultValueAttribute("")]
        [XmlElement("RequiredClasses")]
        public string _requiredClasses;

        [DefaultValueAttribute(false)]
        [XmlElement("Unique")]
        public bool _unique;

        /// <summary>
        /// List of Ids that cannot be used together with this item (other than this item).
        /// Unique should be set to true if this is not empty.
        /// </summary>
        [XmlElement("UniqueId")]
        public List<string> UniqueId { get; set; }

        [DefaultValueAttribute(BindsOn.None)]
        [XmlElement("Bind")]
        public BindsOn _bind;

        [XmlElement("RequiresLightDark")]
        public int _requiresLightDark = 0;

        [XmlElement("ItemModType1")]
        public ItemSlot _itemArmoringHiltBarrelType;

        [XmlElement("ItemModType2")]
        public ItemSlot _itemModType;

        [XmlElement("ItemModType3")]
        public ItemSlot _itemEnhancementType;

        [XmlElement("ItemModType4")]
        public ItemSlot _itemColorCrystalType;

        [XmlElement("ItemModType5")]
        public ItemSlot _itemAugmentType;

        [XmlElement("LocalizedName")]
        public string _localizedName;

        [XmlElement("AllowedRandomSuffix")]
        public List<int> AllowedRandomSuffixes { get; set; }

        #region Location Infos
        [XmlIgnore]
        private bool LocaListPurged = false;
        private ItemLocationList LocationInfos = null;
        public ItemLocationList LocationInfo {
            get {
                if (LocationInfos == null  ) { LocationInfos = new ItemLocationList(); }
                if (LocationInfos.Count < 1) { LocationInfos.Add(new UnknownItem()); }
                if (!string.IsNullOrEmpty(Id) && Id != "0" && !LocaListPurged)
                {
                    // This should only be run once, it's designed to fix bad location lists
                    LocaListPurged = true;
                    int[] counts = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
                    for (int i = 0; i < LocationInfos.Count; ) {
                        if (LocationInfos[i] == null) { LocationInfos.RemoveAt(i); continue; } // Don't iterate
                        else if (LocationInfos[i] is StaticDrop     ) { counts[ 0]++; }
                        else if (LocationInfos[i] is NoSource       ) { counts[ 1]++; }
                        else if (LocationInfos[i] is UnknownItem    ) { counts[ 2]++;
                            if (counts[2] > 1) {
                                LocationInfos.RemoveAt(i);
                                counts[2]--;
                                continue;
                                /*Don't iterate*/
                            }
                        }
                        else if (LocationInfos[i] is WorldDrop      ) { counts[ 3]++; }
                        else if (LocationInfos[i] is PvpItem        ) { counts[ 4]++; }
                        else if (LocationInfos[i] is VendorItem     ) { counts[ 5]++; }
                        else if (LocationInfos[i] is FactionItem    ) { counts[ 6]++; }
                        else if (LocationInfos[i] is CraftedItem    ) { counts[ 7]++; }
                        else if (LocationInfos[i] is QuestItem      ) { counts[ 8]++; }
                        else if (LocationInfos[i] is AchievementItem) { counts[ 9]++; }
                        else if (LocationInfos[i] is ContainerItem  ) { counts[10]++; }
                        i++; // Iterate
                    }
                    while (LocationInfos.Count > 1 && (counts[1] + counts[2]) > 0)
                    {
                        for (int i = 0; i < LocationInfos.Count; i++)
                        {
                            bool removeIt = false;
                            if (LocationInfos[i] is NoSource   ) { counts[1]--; removeIt = true; }
                            if (LocationInfos[i] is UnknownItem) { counts[2]--; removeIt = true; }
                            if (removeIt) { LocationInfos.RemoveAt(i); }
                        }
                    }
                }
                return LocationInfos;
            }
            set {
                LocationInfos = value;
                if (LocationInfos == null) { LocationInfos = new ItemLocationList(); }
                if (LocationInfos.Count < 1 && !string.IsNullOrEmpty(Id) && Id != "0") { LocationInfos.Add(new UnknownItem()); }
                if (!string.IsNullOrEmpty(Id) && Id != "0" && !LocaListPurged)
                {
                    // This should only be run once, it's designed to fix bad location lists
                    LocaListPurged = true;
                    int[] counts = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
                    for (int i = 0; i < LocationInfos.Count; ) {
                        if (LocationInfos[i] is NoSource) {
                            // Replace the NoSource with the Unknown Source
                            LocationInfos[i] = new UnknownItem();
                        }
                        if (LocationInfos[i] == null) { LocationInfos.RemoveAt(i); continue; } // Don't iterate
                        else if (LocationInfos[i] is StaticDrop     ) { counts[ 0]++; }
                        else if (LocationInfos[i] is NoSource       ) { counts[ 1]++; }
                        else if (LocationInfos[i] is UnknownItem    ) { counts[ 2]++;
                            if (counts[2] > 1) {
                                LocationInfos.RemoveAt(i);
                                counts[2]--;
                                continue;
                                /*Don't iterate*/
                            }
                        }
                        else if (LocationInfos[i] is WorldDrop      ) { counts[ 3]++; }
                        else if (LocationInfos[i] is PvpItem        ) { counts[ 4]++; }
                        else if (LocationInfos[i] is VendorItem     ) { counts[ 5]++; }
                        else if (LocationInfos[i] is FactionItem    ) { counts[ 6]++; }
                        else if (LocationInfos[i] is CraftedItem    ) { counts[ 7]++; }
                        else if (LocationInfos[i] is QuestItem      ) { counts[ 8]++; }
                        else if (LocationInfos[i] is AchievementItem) { counts[ 9]++; }
                        else if (LocationInfos[i] is ContainerItem  ) { counts[10]++; }
                        i++; // Iterate
                    }
                    while (LocationInfos.Count > 1 && (counts[1] + counts[2]) > 0) {
                        for (int i = 0; i < LocationInfos.Count; i++) {
                            bool removeIt = false;
                            if (LocationInfos[i] is NoSource   ) { counts[1]--; removeIt = true; }
                            if (LocationInfos[i] is UnknownItem) { counts[2]--; removeIt = true; }
                            if (removeIt) { LocationInfos.RemoveAt(i); }
                        }
                    }
                }
            }
        }
        public string GetFullLocationDesc {
            get {
                string retVal = "";
                if (LocationInfo.Count > 1) {
                    bool first = true;
                    foreach (ItemLocation il in LocationInfo) {
                        if (il == null) { continue; }
                        if (!first) { retVal += " and "; }
                        retVal += il.Description;
                        first = false;
                    }
                } else {
                    retVal = LocationInfo[0].Description;
                }
                return retVal;
            }
        }
        #endregion

        /// <summary>Cost of acquiring the item (i.e. badges, dkp, gold, etc.)</summary>
        [DefaultValueAttribute(0.0f)]
        public float Cost { get; set; }

        [XmlIgnore]
        public DateTime LastChange { get; set; }

        public void InvalidateCachedData()
        {
            Stats.InvalidateSparseData();
            LastChange = DateTime.Now;
        }

        [XmlIgnore]
        public bool Invalid { get; set; }

        public void Delete() { Invalid = true; }

        private string _name;
        [XmlIgnore]
        public string Name
        {
            get {
                if (_localizedName != null && !Rage.Properties.GeneralSettings.Default.Locale.Equals("en")) {
                    return _localizedName;
                } else {
                    return _name;
                }
            }
            set { _name = value; UpdateItemModificationInformation(); }
        }

        [XmlElement("Name")]
        public string EnglishName {
            get { return _name; }
            set { 
                _name = value;
                UpdateItemModificationInformation();
            }
        }

        private int _idAsInt;
        [XmlIgnore]
        public int IdAsInt
        {
            get { return _idAsInt; }
            set { _idAsInt = value; }
        }

        private string _id;
        public string Id {
            get { return _id ?? (Id = "0"); }
            set {
                _id = value;
                int.TryParse(value, out _idAsInt);
                UpdateItemModificationInformation();
            }
        }

        [XmlIgnore]
        public int Rating { get { return _rating; } set { _rating = value; } }
        [XmlIgnore]
        public int ItemLevel { get { return _itemLevel; } set { _itemLevel = value; } }
        [XmlIgnore]
        public string DisplayId { get { return _displayId; } set { _displayId = value; } }
        [XmlIgnore]
        public int DisplaySlot { get { return _displaySlot; } set { _displaySlot = value; } }
        [XmlIgnore]
        public int SlotId
        {
            get
            {
                switch (_slot)
                {
                    case ItemSlot.Head: return 1;
                    case ItemSlot.Chest: return 2;
                    case ItemSlot.Hands: return 3;
                    case ItemSlot.Waist: return 4;
                    case ItemSlot.Legs: return 5;
                    case ItemSlot.Feet: return 6;

                    case ItemSlot.Ear: return 7;
                    case ItemSlot.Implant: return 8;
                    case ItemSlot.Wrist: return 10;
                    case ItemSlot.Relic: return 11;

                    case ItemSlot.OneHand:
                    case ItemSlot.MainHand: return 13;
                    case ItemSlot.OffHand: return 14;
                    default: return 0;
                }
            }
        }
        [XmlIgnore]
        public string IconPath
        {
            get { return (_iconPath == null ? null : _iconPath.ToLower(System.Globalization.CultureInfo.InvariantCulture)); }
            set { _iconPath = value.ToLower(System.Globalization.CultureInfo.InvariantCulture); }
        }

        private ItemSlot _slot;
        public ItemSlot Slot
        {
            get { return _slot; }
            set {
                _slot = value;
                UpdateItemModificationInformation();
            }
        }

        /// <summary>
        /// String version of Slot, to facilitate databinding.
        /// </summary>
        [XmlIgnore]
        public string SlotString
        {
            get { return _slot.ToString(); }
            set 
            { 
                _slot = (ItemSlot)Enum.Parse(typeof(ItemSlot), value, false);
                UpdateItemModificationInformation();
            }
        }

        [XmlIgnore]
        public Stats Stats
        {
            get { return _stats; }
            set { _stats = value; }
        }
        [XmlIgnore]
        public ItemQuality Quality
        {
            get { return _quality; }
            set { _quality = value; }
        }
        [XmlIgnore]
        public string SetName
        {
            get { return _setName; }
            set { _setName = value; }
        }
        [XmlIgnore]
        public ItemFaction Faction
        {
            get { return _faction; }
            set { _faction = value; }
        }
        [XmlIgnore]
        public float DropRate {
            get {
                if (LocationInfo.Count > 0) {
                    foreach (ItemLocation il in LocationInfo) {
                        if (il is StaticDrop && (il as StaticDrop).DropPerc > 0f) {
                            return (il as StaticDrop).DropPerc;
                        }
                    }
                }
                return 0f;
            }
        }

        public bool FitsFaction(CharacterRace race)
        {
            bool fitsFaction = true;
            if (Faction != ItemFaction.Neutral)
            {
                switch (race)
                {
                    // TODO: Set Race to Faction
                    /*case CharacterRace.:
                    case CharacterRace.Dwarf:
                    case CharacterRace.Gnome:
                    case CharacterRace.Human:
                    case CharacterRace.NightElf:
                    case CharacterRace.Worgen:
                        fitsFaction &= Faction == ItemFaction.Republic;
                        break;*/

                    default:
                        fitsFaction &= Faction == ItemFaction.Imperial;
                        break;
                }
            }
            return fitsFaction;
        }

        public bool FitsLightDarkReq(int req)
        {
            bool fits = true;
            if (RequiresLightDark != 0)
            {
                fits &= Math.Abs(RequiresLightDark) <= Math.Abs(req);
            }
            return fits;
        }

        /// <summary>
        /// String version of Faction, to facilitate databinding
        /// </summary>
        [XmlIgnore]
        public string FactionString
        {
            get { return _faction.ToString(); }
            set { _faction = (ItemFaction)Enum.Parse(typeof(ItemFaction), value, false); }
        }
        [XmlIgnore]
        public ItemType Type {
            get { return _type; }
            set { _type = value; }
        }
        /// <summary>
        /// String version of Type, to facilitate databinding
        /// </summary>
        [XmlIgnore]
        public string TypeString {
            get { return _type.ToString(); }
            set { _type = (ItemType)Enum.Parse(typeof(ItemType), value, false); }
        }
        [XmlIgnore]
        public int MinDamage {
            get { return _minDamage; }
            set { _minDamage = value; }
        }
        [XmlIgnore]
        public int MaxDamage {
            get { return _maxDamage; }
            set { _maxDamage = value; }
        }
        [XmlIgnore]
        public ItemDamageType DamageType {
            get { return _damageType; }
            set { _damageType = value; }
        }
        [XmlIgnore]
        public string RequiredClasses {
            get { return _requiredClasses; }
            set { _requiredClasses = value; }
        }
        [XmlIgnore]
        public bool Unique {
            get { return _unique; }
            set { _unique = value; }
        }
        [XmlIgnore]
        public BindsOn Bind
        {
            get { return _bind; }
            set { _bind = value; }
        }
        [XmlIgnore]
        public int RequiresLightDark { get { return _requiresLightDark; } set { if (_requiresLightDark != value) { _requiresLightDark = value; } } }
        [XmlIgnore]
        public ItemSlot ItemArmoringHiltBarrelType
        {
            get { return _itemArmoringHiltBarrelType; }
            set { _itemArmoringHiltBarrelType = value; }
        }
        [XmlIgnore]
        public ItemSlot ItemModType
        {
            get { return _itemModType; }
            set { _itemModType = value; }
        }
        [XmlIgnore]
        public ItemSlot ItemEnhancementType
        {
            get { return _itemEnhancementType; }
            set {
                _itemEnhancementType = value;
            }
        }
        [XmlIgnore]
        public ItemSlot ItemColorCrystalType
        {
            get { return _itemColorCrystalType; }
            set { _itemColorCrystalType = value; }
        }
        [XmlIgnore]
        public ItemSlot ItemAugmentType
        {
            get { return _itemAugmentType; }
            set { _itemAugmentType = value; }
        }
        public ItemSlot GetItemModType(int index)
        {
            switch (index)
            {
                case 1: return ItemArmoringHiltBarrelType;
                case 2: return ItemModType;
                case 3: return ItemEnhancementType;
                case 4: return ItemColorCrystalType;
                case 5: return ItemAugmentType;
                default: return ItemSlot.None;
            }
        }
        [XmlIgnore]
        public bool IsModdable
        {
            get
            {
                return ItemArmoringHiltBarrelType != ItemSlot.None
                    || ItemModType != ItemSlot.None
                    || ItemEnhancementType != ItemSlot.None
                    || ItemColorCrystalType != ItemSlot.None
                    || ItemAugmentType != ItemSlot.None;
            }
        }
        [XmlIgnore]
        public string ItemArmoringHiltBarrelTypeString
        {
            get { return _itemArmoringHiltBarrelType.ToString(); }
            set { _itemArmoringHiltBarrelType = (ItemSlot)Enum.Parse(typeof(ItemSlot), value, false); }
        }
        [XmlIgnore]
        public string ItemModTypeString
        {
            get { return _itemModType.ToString(); }
            set { _itemModType = (ItemSlot)Enum.Parse(typeof(ItemSlot), value, false); }
        }
        [XmlIgnore]
        public string ItemEnhancementTypeString
        {
            get { return _itemEnhancementType.ToString(); }
            set { _itemEnhancementType = (ItemSlot)Enum.Parse(typeof(ItemSlot), value, false); }
        }
        [XmlIgnore]
        public string ItemColorCrystalTypeString
        {
            get { return _itemColorCrystalType.ToString(); }
            set { _itemColorCrystalType = (ItemSlot)Enum.Parse(typeof(ItemSlot), value, false); }
        }
        [XmlIgnore]
        public string ItemAugmentTypeString
        {
            get { return _itemAugmentType.ToString(); }
            set { _itemAugmentType = (ItemSlot)Enum.Parse(typeof(ItemSlot), value, false); }
        }
        [XmlIgnore]
        public string LocalizedName
        {
            get { return _localizedName; }
            set { _localizedName = value; }
        }

        private bool _isItemModification;
        public bool IsItemModification { get { return _isItemModification; } }

        private bool _isArmoring;
        public bool IsArmoring { get { return _isArmoring; } }

        private bool _isBarrel;
        public bool IsBarrel { get { return _isBarrel; } }

        private bool _isHilt;
        public bool IsHilt { get { return _isHilt; } }

        private bool _isMod;
        public bool IsMod { get { return _isMod; } }

        private bool _isEnhancement;
        public bool IsEnhancement { get { return _isEnhancement; } }

        private bool _isColorCrystal;
        public bool IsColorCrystal { get { return _isColorCrystal; } }

        private bool _isAugment;
        public bool IsAugment { get { return _isAugment; } }

        public bool IsLimitedGem
        {
            get
            {
                return _isItemModification && Unique;
            }
        }

        private void UpdateItemModificationInformation()
        {
            _isItemModification = Slot == ItemSlot.Armoring
                || Slot == ItemSlot.Barrel || Slot == ItemSlot.Hilt
                || Slot == ItemSlot.Mod || Slot == ItemSlot.Enhancement
                || Slot == ItemSlot.ColorCrystal || Slot == ItemSlot.Augment;
            _isArmoring = _isItemModification && Item.ItemModificationMatchesSlot(this, ItemSlot.Armoring);
            _isBarrel = _isItemModification && Item.ItemModificationMatchesSlot(this, ItemSlot.Barrel);
            _isHilt = _isItemModification && Item.ItemModificationMatchesSlot(this, ItemSlot.Hilt);
            _isMod = _isItemModification && Item.ItemModificationMatchesSlot(this, ItemSlot.Mod);
            _isEnhancement = _isItemModification && Item.ItemModificationMatchesSlot(this, ItemSlot.Enhancement);
            _isColorCrystal = _isItemModification && Item.ItemModificationMatchesSlot(this, ItemSlot.ColorCrystal);
            _isAugment = _isItemModification && Item.ItemModificationMatchesSlot(this, ItemSlot.Augment);
        }

        public Item() { }
        public Item(string name, ItemQuality quality, ItemType type, string id, string iconPath, ItemSlot slot, string setName, bool unique, Stats stats,
            ItemSlot ItemModType1, ItemSlot ItemModType2, ItemSlot ItemModType3, ItemSlot ItemModType4, ItemSlot ItemModType5,
            int minDamage, int maxDamage, ItemDamageType damageType, string requiredClasses)
        {
            _name = name;
            _id = id;
            _iconPath = iconPath;
            _slot = slot;
            _stats = stats;
            _itemArmoringHiltBarrelType = ItemModType1;
            _itemModType = ItemModType2;
            _itemEnhancementType = ItemModType3;
            _itemColorCrystalType = ItemModType4;
            _itemAugmentType = ItemModType5;
            _setName = setName;
            _quality = quality;
            _type = type;
            _minDamage = minDamage;
            _maxDamage = maxDamage;
            _damageType = damageType;
            _requiredClasses = requiredClasses;
            _unique = unique;
            UpdateItemModificationInformation();
        }

        #region Import from GomLib
        private static ItemType GetItemTypeFromToRItemSubCategory(GomLib.Models.ItemSubCategory i)
        {
            switch (i)
            {
                case GomLib.Models.ItemSubCategory.EquipmentLightArmor:    // ArmorSpec == Light
                    { return ItemType.Light; }
                case GomLib.Models.ItemSubCategory.EquipmentMediumArmor:   // ArmorSpec == Medium
                    { return ItemType.Medium; }
                case GomLib.Models.ItemSubCategory.EquipmentHeavyArmor:    // ArmorSpec == Heavy
                    { return ItemType.Heavy; }
                case GomLib.Models.ItemSubCategory.EquipmentEar:           // Slots Contains EquipHumanEar
                    { return ItemType.Ear; }
                case GomLib.Models.ItemSubCategory.EquipmentImplant:       // Slots Contains EquipHumanImplant
                    { return ItemType.Implant; }
                case GomLib.Models.ItemSubCategory.EquipmentRelic:         // Slots Contains EquipHumanRelic
                    { return ItemType.Relic; }
                case GomLib.Models.ItemSubCategory.WeaponBlasterRifle:     // WeaponSpec == Rifle
                    { return ItemType.BlasterRifle; }
                case GomLib.Models.ItemSubCategory.WeaponSniperRifle:      // WeaponSpec == SniperRifle
                    { return ItemType.SniperRifle; }
                case GomLib.Models.ItemSubCategory.WeaponAssaultCannon:    // WeaponSpec == AssaultCannon
                    { return ItemType.AssaultCannon; }
                case GomLib.Models.ItemSubCategory.WeaponPolesaber:        // WeaponSpec == Polesaber
                    { return ItemType.DoubleBladedLightsaber; }
                case GomLib.Models.ItemSubCategory.WeaponVibroblade:       // WeaponSpec == Vibroblade
                    { return ItemType.Vibrosword; }
                case GomLib.Models.ItemSubCategory.WeaponShotgun:          // WeaponSpec == Shotgun
                    { return ItemType.Shotgun; }
                case GomLib.Models.ItemSubCategory.WeaponElectrostaff:     // WeaponSpec == Electrostaff
                    { return ItemType.Electrostaff; }
                case GomLib.Models.ItemSubCategory.WeaponTechstaff:        // WeaponSpec == ElectrostaffTech
                    { return ItemType.Techblade; }
                case GomLib.Models.ItemSubCategory.WeaponTechblade:        // WeaponSpec == VibrobladeTech
                    { return ItemType.Techblade; }
                case GomLib.Models.ItemSubCategory.WeaponBlasterPistol:    // WeaponSpec == Pistol
                    { return ItemType.BlasterPistol; }
                case GomLib.Models.ItemSubCategory.WeaponLightsaber:       // WeaponSpec == Lightsaber
                    { return ItemType.Lightsaber; }
                case GomLib.Models.ItemSubCategory.WeaponVibroknife:       // WeaponSpec == Vibroknife
                    { return ItemType.Vibroknife; }
                case GomLib.Models.ItemSubCategory.EquipmentFocus:         // ArmorSpec == Focus
                    { return ItemType.Focus; }
                case GomLib.Models.ItemSubCategory.EquipmentGenerator:     // ArmorSpec == Generator
                    { return ItemType.Generator; }
                case GomLib.Models.ItemSubCategory.EquipmentShield:        // ArmorSpec == Shield OR ArmorSpec == ShieldForce
                    { return ItemType.Shield; }
                case GomLib.Models.ItemSubCategory.ItemModColorCrystal:    // EnhancementType == ColorCrystal
                case GomLib.Models.ItemSubCategory.ItemModArmoring:        // EnhancementType == Harness
                case GomLib.Models.ItemSubCategory.ItemModAugment:         // EnhancementType == Modulator
                case GomLib.Models.ItemSubCategory.ItemModMod:             // EnhancementType == Overlay
                case GomLib.Models.ItemSubCategory.ItemModEnhancement:     // EnhancementType == Support
                case GomLib.Models.ItemSubCategory.ItemModBarrel:          // EnhancementType == Barrel
                case GomLib.Models.ItemSubCategory.ItemModHilt:            // EnhancementType == PowerCrystal
                    { return ItemType.None; }
                case GomLib.Models.ItemSubCategory.OtherUnknown:           // Default
                case GomLib.Models.ItemSubCategory.Undefined:
                default: return ItemType.None;
            }
        }
        private static ItemSlot GetItemSlotFromToRSlotType(GomLib.Models.SlotType s)
        {
            switch (s)
            {
                case GomLib.Models.SlotType.EquipHumanFace: { return ItemSlot.Head; }
                case GomLib.Models.SlotType.EquipHumanChest: { return ItemSlot.Chest; }
                case GomLib.Models.SlotType.EquipHumanGlove: { return ItemSlot.Hands; }
                case GomLib.Models.SlotType.EquipHumanBelt: { return ItemSlot.Waist; }
                case GomLib.Models.SlotType.EquipHumanLeg: { return ItemSlot.Legs; }
                case GomLib.Models.SlotType.EquipHumanFoot: { return ItemSlot.Feet; }
                //
                case GomLib.Models.SlotType.EquipHumanEar: { return ItemSlot.Ear; }
                case GomLib.Models.SlotType.EquipHumanImplant: { return ItemSlot.Implant; }
                case GomLib.Models.SlotType.EquipHumanWrist: { return ItemSlot.Wrist; }
                case GomLib.Models.SlotType.EquipHumanRelic: { return ItemSlot.Relic; }
                //
                case GomLib.Models.SlotType.EquipHumanMainHand: { return ItemSlot.MainHand; }
                case GomLib.Models.SlotType.EquipHumanOffHand: { return ItemSlot.OffHand; }
                case GomLib.Models.SlotType.EquipHumanRanged: { return ItemSlot.MainHand; }
                case GomLib.Models.SlotType.EquipHumanRangedPrimary: { return ItemSlot.MainHand; }
                case GomLib.Models.SlotType.EquipHumanRangedSecondary: { return ItemSlot.OffHand; }
                case GomLib.Models.SlotType.EquipHumanRangedTertiary: { return ItemSlot.OneHand; }
                //
                case GomLib.Models.SlotType.EquipHumanShield: { return ItemSlot.OffHand; }
                case GomLib.Models.SlotType.EquipHumanFocus: { return ItemSlot.OffHand; }
                //
                case GomLib.Models.SlotType.EquipHumanCustomRanged: { return ItemSlot.MainHand; }
                case GomLib.Models.SlotType.EquipHumanCustomMelee: { return ItemSlot.MainHand; }

                default: { return ItemSlot.None; }
            }
        }
        private static ItemSlot GetItemSlotFromToRItemSubCategory(GomLib.Models.ItemSubCategory i, GomLib.Models.SlotTypeList s)
        {
            switch (i)
            {
                case GomLib.Models.ItemSubCategory.EquipmentLightArmor:
                case GomLib.Models.ItemSubCategory.EquipmentMediumArmor:
                case GomLib.Models.ItemSubCategory.EquipmentHeavyArmor:
                    {
                        if (s.Count > 0) { return GetItemSlotFromToRSlotType(s[0]); }
                        return ItemSlot.None;
                    }
                case GomLib.Models.ItemSubCategory.EquipmentEar:           // Slots Contains EquipHumanEar
                    { return ItemSlot.Ear; }
                case GomLib.Models.ItemSubCategory.EquipmentImplant:       // Slots Contains EquipHumanImplant
                    { return ItemSlot.Implant; }
                case GomLib.Models.ItemSubCategory.EquipmentRelic:         // Slots Contains EquipHumanRelic
                    { return ItemSlot.Relic; }
                case GomLib.Models.ItemSubCategory.WeaponBlasterRifle:     // WeaponSpec == Rifle
                case GomLib.Models.ItemSubCategory.WeaponSniperRifle:      // WeaponSpec == SniperRifle
                case GomLib.Models.ItemSubCategory.WeaponAssaultCannon:    // WeaponSpec == AssaultCannon
                case GomLib.Models.ItemSubCategory.WeaponPolesaber:        // WeaponSpec == Polesaber
                case GomLib.Models.ItemSubCategory.WeaponVibroblade:       // WeaponSpec == Vibroblade
                case GomLib.Models.ItemSubCategory.WeaponShotgun:          // WeaponSpec == Shotgun
                case GomLib.Models.ItemSubCategory.WeaponElectrostaff:     // WeaponSpec == Electrostaff
                case GomLib.Models.ItemSubCategory.WeaponTrainingSaber:    // WeaponSpec == TrainingSaber
                case GomLib.Models.ItemSubCategory.WeaponTechstaff:        // WeaponSpec == ElectrostaffTech
                case GomLib.Models.ItemSubCategory.WeaponTechblade:        // WeaponSpec == VibrobladeTech
                    { return ItemSlot.MainHand; }
                case GomLib.Models.ItemSubCategory.WeaponBlasterPistol:    // WeaponSpec == Pistol
                case GomLib.Models.ItemSubCategory.WeaponLightsaber:       // WeaponSpec == Lightsaber
                case GomLib.Models.ItemSubCategory.WeaponVibroknife:       // WeaponSpec == Vibroknife
                    { return ItemSlot.OneHand; }
                case GomLib.Models.ItemSubCategory.EquipmentFocus:         // ArmorSpec == Focus
                case GomLib.Models.ItemSubCategory.EquipmentGenerator:     // ArmorSpec == Generator
                case GomLib.Models.ItemSubCategory.EquipmentShield:        // ArmorSpec == Shield OR ArmorSpec == ShieldForce
                    { return ItemSlot.OffHand; }
                case GomLib.Models.ItemSubCategory.ItemModColorCrystal:    // EnhancementType == ColorCrystal
                    { return ItemSlot.ColorCrystal; }
                case GomLib.Models.ItemSubCategory.ItemModArmoring:        // EnhancementType == Harness
                    { return ItemSlot.Armoring; }
                case GomLib.Models.ItemSubCategory.ItemModAugment:         // EnhancementType == Modulator
                    { return ItemSlot.Augment; }
                case GomLib.Models.ItemSubCategory.ItemModMod:             // EnhancementType == Overlay
                    { return ItemSlot.Mod; }
                case GomLib.Models.ItemSubCategory.ItemModEnhancement:     // EnhancementType == Support
                    { return ItemSlot.Enhancement; }
                case GomLib.Models.ItemSubCategory.ItemModBarrel:          // EnhancementType == Barrel
                    { return ItemSlot.Barrel; }
                case GomLib.Models.ItemSubCategory.ItemModHilt:            // EnhancementType == PowerCrystal
                    { return ItemSlot.Hilt; }
                case GomLib.Models.ItemSubCategory.OtherUnknown:           // Default
                case GomLib.Models.ItemSubCategory.Undefined:
                default: return ItemSlot.None;
            }
        }
        private static ItemSlot GetItemSlotFromToREnhancementType(GomLib.Models.EnhancementType i)
        {
            switch (i)
            {
                case GomLib.Models.EnhancementType.Harness: { return ItemSlot.Armoring; }
                case GomLib.Models.EnhancementType.Barrel: { return ItemSlot.Barrel; }
                case GomLib.Models.EnhancementType.PowerCrystal:
                case GomLib.Models.EnhancementType.Hilt: { return ItemSlot.Hilt; }
                case GomLib.Models.EnhancementType.Overlay: { return ItemSlot.Mod; }
                case GomLib.Models.EnhancementType.Support: { return ItemSlot.Enhancement; }
                case GomLib.Models.EnhancementType.ColorCrystal: { return ItemSlot.ColorCrystal; }
                case GomLib.Models.EnhancementType.Modulator: { return ItemSlot.Augment; }
                default: return ItemSlot.None;
            }
        }

        private static string GetSetNameFromToRItemId(int id)
        {
            switch (id)
            {
                case 612467: case 612473: case 612471: case 612466: case 612472:
                case 612587: case 612591: case 612593: case 612586: case 612592:
                case 612707: case 612711: case 612713: case 612706: case 612712: { return "Battlemaster Combat Medic's Gear"; }
                case 612455: case 612452: case 612453: case 612456: case 612454:
                case 612575: case 612574: case 612572: case 612573: case 612576:
                case 612696: case 612695: case 612694: case 612692: case 612693: { return "Battlemaster Combat Tech's Gear"; }
                case 612445: case 612438: case 612443: case 612444: case 612437:
                case 612565: case 612558: case 612563: case 612564: case 612557:
                case 612685: case 612678: case 612683: case 612684: case 612677: { return "Battlemaster Eliminator's Gear"; }
                case 612464: case 612458: case 612462: case 612463: case 612457:
                case 612584: case 612578: case 612582: case 612583: case 612577:
                case 612704: case 612698: case 612702: case 612703: case 612697: { return "Battlemaster Enforcer's Gear"; }
                case 612479: case 612475: case 612474: case 612480: case 612481:
                case 612599: case 612595: case 612594: case 612600: case 612601:
                case 612719: case 612715: case 612714: case 612720: case 612721: { return "Battlemaster Field Medic's Gear"; }
                case 612449: case 612450: case 612448: case 612447: case 612451:
                case 612569: case 612570: case 612568: case 612567: case 612571:
                case 612689: case 612690: case 612688: case 612687: case 612691: { return "Battlemaster Field Tech's Gear"; }
                case 612387: case 612381: case 612388: case 612389: case 612382:
                case 612507: case 612501: case 612508: case 612509: case 612502:
                case 612627: case 612621: case 612628: case 612629: case 612622: { return "Battlemaster Force-Master's Gear"; }
                case 612392: case 612393: case 612394: case 612395: case 612391:
                case 612512: case 612513: case 612514: case 612515: case 612511:
                case 612632: case 612633: case 612634: case 612635: case 612631: { return "Battlemaster Force-Mystic's Gear"; }
                case 612397: case 612401: case 612396: case 612400: case 612402:
                case 612517: case 612521: case 612516: case 612520: case 612522:
                case 612637: case 612641: case 612636: case 612640: case 612642: { return "Battlemaster Stalker's Gear"; }
                case 612484: case 612483: case 612488: case 612490: case 612489:
                case 612604: case 612603: case 612608: case 612610: case 612609:
                case 612647: case 612643: case 612644: case 612648: case 612649: { return "Battlemaster Supercommando's Gear"; }
                case 612407: case 612403: case 612404: case 612408: case 612409:
                case 612527: case 612523: case 612524: case 612528: case 612529:
                case 612724: case 612723: case 612728: case 612730: case 612729: { return "Battlemaster Survivor's Gear"; }
                case 612421: case 612411: case 612422: case 612410: case 612420:
                case 612531: case 612542: case 612530: case 612541: case 612540:
                case 612651: case 612662: case 612650: case 612661: case 612660: { return "Battlemaster Vindicator's Gear"; }
                case 612435: case 612434: case 612433: case 612428: case 612427:
                case 612555: case 612554: case 612553: case 612548: case 612547:
                case 612675: case 612674: case 612673: case 612668: case 612667: { return "Battlemaster War Leader's Gear"; }
                case 612415: case 612424: case 612425: case 612414: case 612423:
                case 612535: case 612544: case 612545: case 612534: case 612543:
                case 612655: case 612664: case 612665: case 612654: case 612663: { return "Battlemaster Weaponmaster's Gear"; }
                case 587931: case 587935: case 587930: case 587936: case 587937:
                case 588051: case 588055: case 588056: case 588050: case 588057:
                case 588306: case 588310: case 588311: case 588305: case 588312: { return "Rakata Combat Medic's Gear"; }
                case 587920: case 587919: case 587917: case 587918: case 587916:
                case 588040: case 588039: case 588038: case 588037: case 588036:
                case 588295: case 588294: case 588293: case 588292: case 588291: { return "Rakata Combat Tech's Gear"; }
                case 587908: case 587901: case 587907: case 587909: case 587902:
                case 588022: case 588028: case 588021: case 588027: case 588029:
                case 588277: case 588283: case 588276: case 588282: case 588284: { return "Rakata Eliminator's Gear"; }
                case 587921: case 587926: case 587922: case 587927: case 587928:
                case 588041: case 588046: case 588042: case 588047: case 588048:
                case 588296: case 588301: case 588297: case 588302: case 588303: { return "Rakata Enforcer's Gear"; }
                case 587939: case 587938: case 587943: case 587944: case 587945:
                case 588059: case 588058: case 588063: case 588064: case 588065:
                case 588314: case 588313: case 588318: case 588319: case 588320: { return "Rakata Field Medic's Gear"; }
                case 587911: case 587914: case 587915: case 587913: case 587912:
                case 588031: case 588034: case 588035: case 588033: case 588032:
                case 588289: case 588286: case 588290: case 588288: case 588287: { return "Rakata Field Tech's Gear"; }
                case 587853: case 587852: case 587851: case 587845: case 587846:
                case 587965: case 587966: case 587973: case 587971: case 587972:
                case 588227: case 588228: case 588226: case 588220: case 588221: { return "Rakata Force-Master's Gear"; }
                case 587859: case 587856: case 587855: case 587857: case 587858:
                case 587979: case 587976: case 587975: case 587977: case 587978:
                case 588234: case 588231: case 588230: case 588232: case 588233: { return "Rakata Force-Mystic's Gear"; }
                case 587861: case 587864: case 587865: case 587860: case 587866:
                case 587981: case 587984: case 587980: case 587985: case 587986:
                case 588236: case 588239: case 588235: case 588240: case 588241: { return "Rakata Stalker's Gear"; }
                case 587948: case 587953: case 587947: case 587952: case 587954:
                case 588068: case 588073: case 588067: case 588072: case 588074:
                case 588323: case 588328: case 588322: case 588327: case 588329: { return "Rakata Supercommando's Gear"; }
                case 587872: case 587868: case 587867: case 587871: case 587873:
                case 587992: case 587988: case 587987: case 587991: case 587993:
                case 588247: case 588243: case 588242: case 588246: case 588248: { return "Rakata Survivor's Gear"; }
                case 587874: case 587875: case 587884: case 587885: case 587886:
                case 587994: case 587995: case 588004: case 588005: case 588006:
                case 588249: case 588250: case 588259: case 588260: case 588261: { return "Rakata Vindicator's Gear"; }
                case 587898: case 587899: case 587892: case 587891: case 587897:
                case 588018: case 588019: case 588012: case 588011: case 588017:
                case 588273: case 588274: case 588267: case 588266: case 588272: { return "Rakata War Leader's Gear"; }
                case 587888: case 587889: case 587878: case 587887: case 587879:
                case 588008: case 588009: case 587998: case 588007: case 587999:
                case 588263: case 588264: case 588253: case 588262: case 588254: { return "Rakata Weaponmaster's Gear"; }
                default: { break; }
            }
            return "";
        }

        public static Item FromToRItem(GomLib.Models.Item dItem)
        {
            float armor = 0f;
            float[] weapon = { 0, 0, 0, 0, 0, 0 };
            bool IsAdvWeapon = false;

            /*if (dItem.SubCategory == GomLib.Models.ItemSubCategory.ItemModArmoring) {
                armor = dItem.Rating;
            }
            else*/ if (dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponBlasterPistol
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponBlasterRifle
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponSniperRifle
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponAssaultCannon
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponLightsaber
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponPolesaber
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponVibroblade
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponVibroknife
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponShotgun
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponElectrostaff
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponTrainingSaber
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponTechstaff
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.WeaponTechblade
                )
            {
                IsAdvWeapon = (int)dItem.Quality >= (int)GomLib.Models.ItemQuality.Artifact;
                // Possible Stats: MaxWeaponDamage, MinWeaponDamage, ForcePowerRating, TechPowerRating
                if (IsAdvWeapon) { dItem.ItemLevel += 1; }
                weapon[0] = GomLib.Tables.WeaponPerLevel.GetStat(dItem, GomLib.Models.Stat.MinWeaponDamage);
                weapon[1] = GomLib.Tables.WeaponPerLevel.GetStat(dItem, GomLib.Models.Stat.MaxWeaponDamage);
                weapon[2] = GomLib.Tables.WeaponPerLevel.GetStat(dItem, GomLib.Models.Stat.ForcePowerRating);
                weapon[3] = GomLib.Tables.WeaponPerLevel.GetStat(dItem, GomLib.Models.Stat.TechPowerRating);
                if (IsAdvWeapon) { dItem.ItemLevel -= 1; }
            }
            else if (dItem.SubCategory == GomLib.Models.ItemSubCategory.EquipmentFocus
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.EquipmentGenerator
                )
            {
                // Possible Stats: ForcePowerRating, TechPowerRating
                weapon[2] = (float)Math.Round(GomLib.Tables.WeaponPerLevel.GetStat(GomLib.Models.WeaponSpec.Lightsaber, dItem.ItemLevel, dItem.Quality, GomLib.Models.Stat.ForcePowerRating) * 2f / 3f, 0);
                weapon[3] = (float)Math.Round(GomLib.Tables.WeaponPerLevel.GetStat(GomLib.Models.WeaponSpec.Lightsaber, dItem.ItemLevel, dItem.Quality, GomLib.Models.Stat.TechPowerRating) * 2f / 3f, 0);
            }
            else if (dItem.SubCategory == GomLib.Models.ItemSubCategory.EquipmentShield)
            {
                // Possible Stats: ForcePowerRating, TechPowerRating, Shield Chance, Shield Absorb
                //weapon[2] = GomLib.Tables.ArmorPerLevel.GetArmor(dItem);
                weapon[2] = (float)Math.Round(GomLib.Tables.WeaponPerLevel.GetStat(GomLib.Models.WeaponSpec.Lightsaber, dItem.ItemLevel, dItem.Quality, GomLib.Models.Stat.ForcePowerRating) * 2f / 3f, 0);
                weapon[3] = (float)Math.Round(GomLib.Tables.WeaponPerLevel.GetStat(GomLib.Models.WeaponSpec.Lightsaber, dItem.ItemLevel, dItem.Quality, GomLib.Models.Stat.TechPowerRating) * 2f / 3f, 0);
                weapon[4] = 0.05f;
                weapon[5] = 0.20f;
            }
            else if (dItem.SubCategory == GomLib.Models.ItemSubCategory.EquipmentEar
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.EquipmentImplant
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.EquipmentRelic
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.ItemModArmoring // 
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.ItemModBarrel
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.ItemModHilt
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.ItemModMod
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.ItemModEnhancement
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.ItemModColorCrystal
                || dItem.SubCategory == GomLib.Models.ItemSubCategory.ItemModAugment)
            {
                // Do nothing
            }
            else
            {
                armor = GomLib.Tables.ArmorPerLevel.GetArmor(dItem);
            }

            string setName = GetSetNameFromToRItemId((int)dItem.Id);

            Stats stats = new Stats() { Armor = armor, ForcePower = weapon[2], TechPower = weapon[3], Shield = weapon[4], Absorb = weapon[5] };
            foreach (GomLib.Models.ItemStat iStat in dItem.StatModifiers)
            {
                string statName = iStat.Stat.ToString()
                    .Replace(" ", "")
                    .Replace("ArmorRating", "Armor")
                    .Replace("CriticalChanceRating", "CritRating")
                    .Replace("AttackPowerRating", "Power")
                    .Replace("Absorption", "Absorb")
                    .Replace("GlanceRating", "ShieldRating");
                if (!Stats.StatsContainsThisStatName(statName))
                {
                    System.Windows.MessageBox.Show(statName);
                }
                stats.AssignStatByName(statName, iStat.Modifier);
            }
            ItemSlot mod1 = ItemSlot.None;
            ItemSlot mod2 = ItemSlot.None;
            ItemSlot mod3 = ItemSlot.None;
            ItemSlot mod4 = ItemSlot.None;
            if (dItem.EnhancementSlots != null)
            {
                foreach (var i in dItem.EnhancementSlots)
                {
                    var s = GetItemSlotFromToREnhancementType(i.Slot);
                    switch (s)
                    {
                        case ItemSlot.Armoring:
                        case ItemSlot.Hilt:
                        case ItemSlot.Barrel: { mod1 = s; break; }
                        case ItemSlot.Mod: { mod2 = s; break; }
                        case ItemSlot.Enhancement: { mod3 = s; break; }
                        case ItemSlot.ColorCrystal: { mod4 = s; break; }
                        default: { break; }
                    }
                }
            }
            string reqClasses = "";
            if (dItem.RequiredClasses != null && dItem.RequiredClasses.Count > 0)
            {
                foreach (GomLib.Models.ClassSpec cs in dItem.RequiredClasses.Where(i => i.IsPlayerClass))
                {
                    reqClasses += cs.Name + ",";
                }
                reqClasses = reqClasses.TrimEnd(',');
            }
            BindsOn bind = BindsOn.None;
            if (dItem.Binding == GomLib.Models.ItemBindingRule.Pickup) { bind = BindsOn.BoP; }
            if (dItem.Binding == GomLib.Models.ItemBindingRule.Equip) { bind = BindsOn.BoE; }
            int reqLightDark = 0;
            if (dItem.RequiredAlignmentTier != 0 && dItem.RequiredAlignmentTier < 20) {
                reqLightDark = dItem.RequiredAlignmentTier;
            }
            return new Item(dItem.Name, // Validated for Mods
                (ItemQuality)(int)dItem.Quality, // Validated for Mods
                GetItemTypeFromToRItemSubCategory(dItem.SubCategory),
                dItem.Id.ToString(),
                dItem.Icon,
                GetItemSlotFromToRItemSubCategory(dItem.SubCategory, dItem.Slots),
                setName,
                dItem.UniqueLimit != 0,
                stats,
                mod1, // Armoring/Hilt/Barrel
                mod2, // Mod
                mod3, // Enhancement
                mod4, // Color Crystal
                ItemSlot.None, // Augment should be ItemSlot.None by Default, other methods make this slot available
                (int)weapon[0], // Min Damage
                (int)weapon[1], // Max Damage
                (ItemDamageType)(int)dItem.DamageType,
                reqClasses)
                {
                    ItemLevel = dItem.ItemLevel,
                    Bind = bind,
                    RequiresLightDark = reqLightDark,
                    Rating = GomLib.Tables.ItemRating.TableData[dItem.ItemLevel + (IsAdvWeapon ? 1 : 0)][(GomLib.Models.ItemQuality)(int)dItem.Quality],
                };
        }
        #endregion

        public Item Clone()
        {
            return new Item()
            {
                Name = this.Name,
                Quality = this.Quality,
                Id = this.Id,
                IconPath = this.IconPath,
                Slot = this.Slot,
                Stats = this.Stats.Clone(),
                ItemArmoringHiltBarrelType = this.ItemArmoringHiltBarrelType,
                ItemModType = this.ItemModType,
                ItemEnhancementType = this.ItemEnhancementType,
                ItemColorCrystalType = this.ItemColorCrystalType,
                ItemAugmentType = this.ItemAugmentType,
                SetName = this.SetName,
                Type = this.Type,
                MinDamage = this.MinDamage,
                MaxDamage = this.MaxDamage,
                DamageType = this.DamageType,
                RequiredClasses = this.RequiredClasses,
                Unique = this.Unique,
                UniqueId = new List<string>(this.UniqueId ?? (this.UniqueId = new List<string>() { })),
                LocalizedName = this.LocalizedName
            };
        }

        public override string ToString()
        {
            return (!string.IsNullOrEmpty(this.Name) ? this.Name : this.Id) + ": " + 
                (this.Bind != BindsOn.None ? (this.Bind + " ") : string.Empty) +
                this.Stats.ToString();
        }

        public static bool ItemModificationMatchesSlot(Item itemMod, ItemSlot slotColor)
        {
            switch (slotColor)
            {
                case ItemSlot.Armoring: return itemMod != null && (itemMod.Slot == ItemSlot.Armoring);
                case ItemSlot.Hilt: return itemMod != null && (itemMod.Slot == ItemSlot.Hilt);
                case ItemSlot.Barrel: return itemMod != null && (itemMod.Slot == ItemSlot.Barrel);
                case ItemSlot.Mod: return itemMod != null && (itemMod.Slot == ItemSlot.Mod);
                case ItemSlot.Enhancement: return itemMod != null && (itemMod.Slot == ItemSlot.Enhancement);
                case ItemSlot.ColorCrystal: return itemMod != null && (itemMod.Slot == ItemSlot.ColorCrystal);
                case ItemSlot.Augment: return itemMod != null && (itemMod.Slot == ItemSlot.Augment);
                default: return itemMod == null || itemMod.Slot != ItemSlot.Augment;
            }
        }

        public static Dictionary<ItemSlot, CharacterSlot> DefaultSlotMap { get; private set; }
        static Item()
        {
            Dictionary<ItemSlot, CharacterSlot> list = new Dictionary<ItemSlot, CharacterSlot>();
            foreach (ItemSlot iSlot in EnumHelper.GetValues(typeof(ItemSlot)))
            {
                list[iSlot] = CharacterSlot.None;
            }
            list[ItemSlot.Head] = CharacterSlot.Head;
            list[ItemSlot.Chest] = CharacterSlot.Chest;
            list[ItemSlot.Hands] = CharacterSlot.Hands;
            list[ItemSlot.Waist] = CharacterSlot.Waist;
            list[ItemSlot.Legs] = CharacterSlot.Legs;
            list[ItemSlot.Feet] = CharacterSlot.Feet;

            list[ItemSlot.Ear] = CharacterSlot.Ear;
            list[ItemSlot.Implant] = CharacterSlot.Implant1;
            //list[ItemSlot.Implant] = CharacterSlot.Implant2;
            list[ItemSlot.Wrist] = CharacterSlot.Wrist;
            list[ItemSlot.Relic] = CharacterSlot.Relic1;
            //list[ItemSlot.Relic] = CharacterSlot.Relic2;

            list[ItemSlot.MainHand] = CharacterSlot.MainHand;
            list[ItemSlot.OffHand] = CharacterSlot.OffHand;
            list[ItemSlot.OneHand] = CharacterSlot.MainHand;

            list.OrderBy(kvp => (int)kvp.Key);
            DefaultSlotMap = list;
        }

        public bool FitsInSlot(CharacterSlot charSlot)
        {
            //And if I fell with all the strength I held inside...
            switch (charSlot)
            {
                case CharacterSlot.Head: return this.Slot == ItemSlot.Head;
                case CharacterSlot.Chest: return this.Slot == ItemSlot.Chest;
                case CharacterSlot.Hands: return this.Slot == ItemSlot.Hands;
                case CharacterSlot.Waist: return this.Slot == ItemSlot.Waist;
                case CharacterSlot.Legs: return this.Slot == ItemSlot.Legs;
                case CharacterSlot.Feet: return this.Slot == ItemSlot.Feet;

                case CharacterSlot.Ear: return this.Slot == ItemSlot.Ear;
                case CharacterSlot.Implant1:
                case CharacterSlot.Implant2: return this.Slot == ItemSlot.Implant;
                case CharacterSlot.Wrist: return this.Slot == ItemSlot.Wrist;
                case CharacterSlot.Relic1:
                case CharacterSlot.Relic2: return this.Slot == ItemSlot.Relic;

                case CharacterSlot.MainHand: return this.Slot == ItemSlot.MainHand || this.Slot == ItemSlot.OneHand;
                case CharacterSlot.OffHand: return this.Slot == ItemSlot.OffHand || this.Slot == ItemSlot.OneHand;

                case CharacterSlot.Armoring: return this.Slot == ItemSlot.Armoring;
                case CharacterSlot.Barrel: return this.Slot == ItemSlot.Barrel;
                case CharacterSlot.Hilt: return this.Slot == ItemSlot.Hilt;
                case CharacterSlot.Mod: return this.Slot == ItemSlot.Mod;
                case CharacterSlot.Enhancement: return this.Slot == ItemSlot.Enhancement;
                case CharacterSlot.ColorCrystal: return this.Slot == ItemSlot.ColorCrystal;
                case CharacterSlot.Augment: return this.Slot == ItemSlot.Augment;

                default: return false;
            }
        }

        public bool FitsInSlot(CharacterSlot charSlot, Character character)
        {
            return Calculations.ItemFitsInSlot(this, character, charSlot, false);
        }

        public bool FitsInSlot(CharacterSlot charSlot, Character character, bool ignoreUnique)
        {
            return Calculations.ItemFitsInSlot(this, character, charSlot, ignoreUnique);
        }

        public bool MeetsRequirements(Character character)
        {
            bool temp;
            return MeetsRequirements(character, out temp);
        }

        public static bool ItemsAreConsideredUniqueEqual(Item itema, Item itemb)
        {
            return (object)itema != null
                && (object)itemb != null
                && itema.Unique
                && (itema.Id == itemb.Id
                    || (itema.UniqueId != null
                        && itema.UniqueId.Contains(itemb.Id)));
        }

        public static bool OptimizerManagedVolatiliy { get; set; }

        /// <summary>
        /// Checks whether item meets the requirements to activate its stats.
        /// </summary>
        /// <param name="character">Character for which we are checking the requirements.</param>
        /// <param name="volatileRequirements">This is set to true for items that depend on requirements not local to the item itself.</param>
        /// <returns>True if the item meets the requirements.</returns>
        public bool MeetsRequirements(Character character, out bool volatileRequirements)
        {
            volatileRequirements = false;
            bool meetsRequirements = true;

            if (this.Slot == ItemSlot.Armoring)
            {
                volatileRequirements = true;
                if (character == null) { return true; }
            }
            else if (!OptimizerManagedVolatiliy)
            {
                volatileRequirements = false;
                meetsRequirements = true;
            }

            return meetsRequirements;
        }

        public static Item LoadFromId(string id) { return LoadFromId(id, false, true, false, false); }
        public static Item LoadFromId(string id, bool forceRefresh, bool raiseEvent, bool useTorhead, bool usePTR) { return LoadFromId(id, forceRefresh, raiseEvent, useTorhead, usePTR, Rage.Properties.GeneralSettings.Default.Locale); }
        public static Item LoadFromId(string id, bool forceRefresh, bool raiseEvent, bool useTorhead, bool usePTR, string locale) { return LoadFromId(id, forceRefresh, raiseEvent, useTorhead, usePTR, locale, "cata"); }
        public static Item LoadFromId(string id, bool forceRefresh, bool raiseEvent, bool useTorhead, bool usePTR, string locale, string torheadSite)
        {
            Item cachedItem = ItemCache.FindItemById(id);
            #if DEBUG
            string oldItemStats = "";
            string oldItemSource = "";
            List<ItemLocation> oldItemLoc = null;
            if (cachedItem != null && forceRefresh){
                oldItemStats  = cachedItem.ToString().Split(':')[1];
                oldItemLoc    = cachedItem.LocationInfo;
                oldItemSource = cachedItem.GetFullLocationDesc;
            }
            #endif

            if (cachedItem != null && !forceRefresh) return cachedItem;
            else if (useTorhead)
            {
                /*TorheadService torheadService = new TorheadService();
                torheadService.GetItemCompleted += new EventHandler<EventArgs<Item>>(torheadService_GetItemCompleted);
                torheadService.GetItemAsync(id, usePTR);*/

                if (cachedItem != null) return cachedItem;
                else
                {
                    Item tempItem = new Item();
                    tempItem.Name = "[Empty Item, fill in stats]";
                    tempItem.Id = id;
                    ItemCache.AddItem(tempItem, raiseEvent);
                    return tempItem;
                }
            }
            else
            {
                return null;
                /*ElitistArmoryService armoryService = new ElitistArmoryService();
                armoryService.GetItemCompleted += new EventHandler<EventArgs<Item>>(armoryService_GetItemCompleted);
                armoryService.GetItemAsync(id);
                
                if (cachedItem != null) return cachedItem;
                else
                {
                    Item tempItem = new Item();
                    tempItem.Name = "[Item Not Found - Automatic Armory Item Lookups Coming Soon]";
                    tempItem.Id = id;
                    ItemCache.AddItem(tempItem, raiseEvent);
                    return tempItem;
                }*/
            }
        }

        /*private static void armoryService_GetItemCompleted(object sender, EventArgs<Item> e)
        {
            if (e.Value != null)
                ItemCache.AddItem(e.Value, true);
            ((ElitistArmoryService)sender).GetItemCompleted -= new EventHandler<EventArgs<Item>>(armoryService_GetItemCompleted);
        }*/

        private static void torheadService_GetItemCompleted(object sender, EventArgs<Item> e)
        {
            if (e.Value != null)
                ItemCache.AddItem(e.Value, true);
            /*((TorheadService)sender).GetItemCompleted -= new EventHandler<EventArgs<Item>>(torheadService_GetItemCompleted);*/
        }

        /// <summary>Used by optimizer</summary>
        [XmlIgnore]
        internal List<Optimizer.ItemAvailabilityInformation> AvailabilityInformation;

        #region IComparable<Item> Members

        public int CompareTo(Item other)
        {
            return ToString().CompareTo(other.ToString());
        }

        #endregion
    }
    #endregion

    #region ItemInstance
    // to make our lives more tolerable, ItemInstance is exactly what it implies
    // it is a single instance of an item, it is not to be shared between multiple characters
    // or whatever, at least if you don't know what you are doing
    // if for whatever reason you reuse the same instance treat it as read only
    public class ItemInstance : IComparable<ItemInstance>
    {
        [XmlElement("Id")]
        public string _id;
        [DefaultValueAttribute("0")]
        [XmlElement("ItemMod1Id")]
        public string _itemMod1Id;
        [DefaultValueAttribute("0")]
        [XmlElement("ItemMod2Id")]
        public string _itemMod2Id;
        [DefaultValueAttribute("0")]
        [XmlElement("ItemMod3Id")]
        public string _itemMod3Id;
        [DefaultValueAttribute("0")]
        [XmlElement("ItemMod4Id")]
        public string _itemMod4Id;
        [DefaultValueAttribute("0")]
        [XmlElement("ItemMod5Id")]
        public string _itemMod5Id;
        [DefaultValueAttribute("0")]
        [XmlElement("RandomSuffixId")]
        public string _randomSuffixId;

        // Used by optimizer
        [XmlIgnore]
        internal Optimizer.ItemAvailabilityInformation ItemAvailabilityInformation { get; set; }

        [XmlIgnore]
        public string Id
        {
            get { return _id ?? (_id = "0"); }
            set { _id = value; OnIdsChanged(); }
        }
        [XmlIgnore]
        public string ItemMod1Id
        {
            get { return _itemMod1Id ?? (_itemMod1Id = "0"); }
            set { _itemMod1Id = value; OnIdsChanged(); }
        }
        [XmlIgnore]
        public string ItemMod2Id
        {
            get { return _itemMod2Id ?? (_itemMod2Id = "0"); }
            set { _itemMod2Id = value; OnIdsChanged(); }
        }
        [XmlIgnore]
        public string ItemMod3Id
        {
            get { return _itemMod3Id ?? (_itemMod3Id = "0"); }
            set { _itemMod3Id = value; OnIdsChanged(); }
        }
        [XmlIgnore]
        public string ItemMod4Id
        {
            get { return _itemMod4Id ?? (_itemMod4Id = "0"); }
            set { _itemMod4Id = value; OnIdsChanged(); }
        }
        [XmlIgnore]
        public string ItemMod5Id
        {
            get { return _itemMod5Id ?? (_itemMod5Id = "0"); }
            set { _itemMod5Id = value; OnIdsChanged(); }
        }
        [XmlIgnore]
        public string RandomSuffixId
        {
            get { return _randomSuffixId; }
            set { _randomSuffixId = value; OnIdsChanged(); }
        }

        [DefaultValueAttribute(false)]
        public bool ForceDisplay { get; set; }

        public event EventHandler IdsChanged;
        private void OnIdsChanged()
        {
            _itemModdedId = string.Empty;
            InvalidateCachedData();
            
            if (IdsChanged != null) IdsChanged(this, null);
        }

        [XmlIgnore]
        private Item _itemCached = null;
        [XmlIgnore]
        public Item Item
        {
            get
            {
                if (Id == "0") return null;
                if (_itemCached == null || _itemCached.Id != Id || _itemCached.Invalid)
                {
                    // don't need to raise event, it only triggers when item is not present and just for the blank item
                    // the real event when item is loaded from web asynchronously is always triggered
                    _itemCached = Item.LoadFromId(Id, false, false, true, false); // changed to Torhead until we have battle.net parsing working
                    //_itemCached = Item.LoadFromId(Id);
                    // after a temp item is replaced when we fetch data from Knotor the old one will be marked invalid
                    InvalidateCachedData();
                }
                return _itemCached;
            }
            set
            {
                _itemCached = value;
                if (value == null)
                    Id = "0";
                else
                    Id = value.Id;
            }
        }

        [XmlIgnore]
        private Item _itemMod1Cached = null;
        [XmlIgnore]
        public Item ItemMod1
        {
            get
            {
                if (ItemMod1Id == "0") return null;
                if (_itemMod1Cached == null || _itemMod1Cached.Id != ItemMod1Id || _itemMod1Cached.Invalid)
                {
                    _itemMod1Cached = Item.LoadFromId(ItemMod1Id);
                }
                return _itemMod1Cached;
            }
            set
            {
                _itemMod1Cached = value;
                if (value == null)
                    ItemMod1Id = "0";
                else
                    ItemMod1Id = value.Id;
            }
        }

        [XmlIgnore]
        private Item _itemMod2Cached = null;
        [XmlIgnore]
        public Item ItemMod2
        {
            get
            {
                if (ItemMod2Id == "0") return null;
                if (_itemMod2Cached == null || _itemMod2Cached.Id != ItemMod2Id || _itemMod2Cached.Invalid)
                {
                    _itemMod2Cached = Item.LoadFromId(ItemMod2Id);
                }
                return _itemMod2Cached;
            }
            set
            {
                _itemMod2Cached = value;
                if (value == null)
                    ItemMod2Id = "0";
                else
                    ItemMod2Id = value.Id;
            }
        }

        [XmlIgnore]
        private Item _itemMod3Cached = null;
        [XmlIgnore]
        public Item ItemMod3
        {
            get
            {
                if (ItemMod3Id == "0") return null;
                if (_itemMod3Cached == null || _itemMod3Cached.Id != ItemMod3Id || _itemMod3Cached.Invalid)
                {
                    _itemMod3Cached = Item.LoadFromId(ItemMod3Id);
                }
                return _itemMod3Cached;
            }
            set
            {
                _itemMod3Cached = value;
                if (value == null)
                    ItemMod3Id = "0";
                else
                    ItemMod3Id = value.Id;
            }
        }

        [XmlIgnore]
        private Item _itemMod4Cached = null;
        [XmlIgnore]
        public Item ItemMod4
        {
            get
            {
                if (ItemMod4Id == "0") return null;
                if (_itemMod4Cached == null || _itemMod4Cached.Id != ItemMod4Id || _itemMod4Cached.Invalid)
                {
                    _itemMod4Cached = Item.LoadFromId(ItemMod4Id);
                }
                return _itemMod4Cached;
            }
            set
            {
                _itemMod4Cached = value;
                if (value == null)
                    ItemMod4Id = "0";
                else
                    ItemMod4Id = value.Id;
            }
        }

        [XmlIgnore]
        private Item _itemMod5Cached = null;
        [XmlIgnore]
        public Item ItemMod5
        {
            get
            {
                if (ItemMod5Id == "0") return null;
                if (_itemMod5Cached == null || _itemMod5Cached.Id != ItemMod5Id || _itemMod5Cached.Invalid)
                {
                    _itemMod5Cached = Item.LoadFromId(ItemMod5Id);
                }
                return _itemMod5Cached;
            }
            set
            {
                _itemMod5Cached = value;
                if (value == null)
                    ItemMod5Id = "0";
                else
                    ItemMod5Id = value.Id;
            }
        }

        // 1-based index
        public Item GetItemMod(int index)
        {
            switch (index)
            {
                case 1: return ItemMod1;
                case 2: return ItemMod2;
                case 3: return ItemMod3;
                case 4: return ItemMod4;
                case 5: return ItemMod5;
                default: return null;
            }
        }

        public void SetItemMod(int index, Item value)
        {
            switch (index)
            {
                case 1: ItemMod1 = value; break;
                case 2: ItemMod2 = value; break;
                case 3: ItemMod3 = value; break;
                case 4: ItemMod4 = value; break;
                case 5: ItemMod5 = value; break;
            }
        }

        [XmlIgnore]
        private string _itemModdedId = string.Empty;
        [XmlIgnore]
        public string ItemModdedId
        {
            get
            {
                if (_itemModdedId.Length == 0) // _gemmedId is never null
                {
                    _itemModdedId = string.Format("{0}.{1}.{2}.{3}.{4}.{5}.{6}",
                        Id, RandomSuffixId, ItemMod1Id, ItemMod2Id, ItemMod3Id, ItemMod4Id, ItemMod5Id);
                }
                return _itemModdedId;
            }
            set
            {
                if (value == null) _itemModdedId = string.Empty;
                else _itemModdedId = value;
                string[] ids = _itemModdedId.Split('.');
                if (ids.Length == 6)
                {
                    // gemmed id without random suffix
                    ids = new string[] { ids[0], "0", ids[1], ids[2], ids[3], ids[4], ids[5], ids[6] };
                }
                // gemmed id without random suffix
                _id = ids[0];
                _randomSuffixId = ids[1];
                _itemMod1Id = ids[2];
                _itemMod2Id = ids[3];
                _itemMod3Id = ids[4];
                _itemMod4Id = ids[5];
                _itemMod5Id = ids[6];
                OnIdsChanged();
            }
        }

        [XmlIgnore]
        public string SuffixedId
        {
            get
            {
                return Id + "." + RandomSuffixId;
            }
        }

        public ItemInstance() { }
        public ItemInstance(string gemmedId)
        {
            string[] ids = gemmedId.Split('.');
            _id = ids[0];
            _randomSuffixId = ids[1];
            _itemMod1Id = ids[2];
            _itemMod2Id = ids[3];
            _itemMod3Id = ids[4];
            _itemMod4Id = ids[5];
            _itemMod5Id = ids[6];
        }
        public ItemInstance(string id, string randomSuffixId, string itemMod1Id, string itemMod2Id, string itemMod3Id, string itemMod4Id, string itemMod5Id)
        {
            _id = id;
            _randomSuffixId = randomSuffixId;
            _itemMod1Id = itemMod1Id;
            _itemMod2Id = itemMod2Id;
            _itemMod3Id = itemMod3Id;
            _itemMod4Id = itemMod4Id;
            _itemMod5Id = itemMod5Id;
        }
        public ItemInstance(Item item, int randomSuffixId, Item itemMod1, Item itemMod2, Item itemMod3, Item itemMod4, Item itemMod5)
        {
            // this code path is used a lot, optimize for performance
            _itemCached = item;
            _randomSuffixId = randomSuffixId.ToString();
            _itemMod1Cached = itemMod1;
            _itemMod2Cached = itemMod2;
            _itemMod3Cached = itemMod3;
            _itemMod4Cached = itemMod4;
            _itemMod5Cached = itemMod5;
            _id = item != null ? item.Id : "0";
            _itemMod1Id = itemMod1 != null ? itemMod1.Id : "0";
            _itemMod2Id = itemMod2 != null ? itemMod2.Id : "0";
            _itemMod3Id = itemMod3 != null ? itemMod3.Id : "0";
            _itemMod4Id = itemMod4 != null ? itemMod4.Id : "0";
            _itemMod5Id = itemMod5 != null ? itemMod5.Id : "0";
            OnIdsChanged();
        }

        public ItemInstance Clone()
        {
            return new ItemInstance()
            {
                Item = this.Item,
                RandomSuffixId = this.RandomSuffixId,
                ItemMod1 = this.ItemMod1,
                ItemMod2 = this.ItemMod2,
                ItemMod3 = this.ItemMod3,
                ItemMod4 = this.ItemMod4,
                ItemMod5 = this.ItemMod5,
                ItemAvailabilityInformation = this.ItemAvailabilityInformation // batch tools relies on this
            };
        }

        public override string ToString()
        {
            string summary;
            summary = this.Item.Name;
            if (RandomSuffixId != "0")
            {
                summary += " " /*+ RandomSuffix.GetSuffix(RandomSuffixId)*/;
            }
            summary += ": ";
            summary += this.GetTotalStats().ToString();
            //summary += Stats.ToString();
            //summary += Sockets.ToString();
            if (summary.EndsWith(", ")) summary = summary.Substring(0, summary.Length - 2);

            if ((Item.ItemArmoringHiltBarrelType != ItemSlot.None && ItemMod1Id == "0") ||
                (Item.ItemModType != ItemSlot.None && ItemMod2Id == "0") ||
                (Item.ItemEnhancementType != ItemSlot.None && ItemMod3Id == "0") ||
                (Item.ItemColorCrystalType != ItemSlot.None && ItemMod4Id == "0") ||
                (Item.ItemAugmentType != ItemSlot.None && ItemMod5Id == "0"))
                summary += " [EMPTY MODIFICATION SLOTS]";

            return summary;
        }

        public string ToItemString()
        {
            return "item:" + this.Id + ":" +
                RandomSuffixId + ":" + 
                GemIDConverter.ConvertGemItemIDToEnchantID(ItemMod1Id) + ":" + 
                GemIDConverter.ConvertGemItemIDToEnchantID(ItemMod2Id) + ":" + 
                GemIDConverter.ConvertGemItemIDToEnchantID(ItemMod3Id) + ":" +
                GemIDConverter.ConvertGemItemIDToEnchantID(ItemMod4Id) + ":" +
                GemIDConverter.ConvertGemItemIDToEnchantID(ItemMod5Id);
        }

        public int SlotId
        {
            get
            {
                return Item == null ? 0 : Item.SlotId;
            }
        }

        public string DisplayId
        {
            get
            {
                return Item == null ? "0" : Item.DisplayId;
            }
        }

        public int DisplaySlot
        {
            get
            {
                return Item == null ? 0 : Item.DisplaySlot;
            }
        }

        // caching policy: cache total stats only for items that don't have global requirements
        // value should not change if it relies on data other than from this item
        // assume there is no stat editing happening in code other than in item editor
        // invalidate on id changes, invalidate when item is opened for editing
        // invalidate all items when any gem is opened for editing
        // invalidate 
        private Stats cachedTotalStats = null;
        private DateTime cachedTime; 
        public void InvalidateCachedData()
        {
            cachedTotalStats = null;
        }

        public Stats GetTotalStats() { return AccumulateTotalStats(null, null); }
        public Stats GetTotalStats(Character character) { return AccumulateTotalStats(character, null); }
#if SILVERLIGHT
        public Stats AccumulateTotalStats(Character character, Stats unsafeStatsAccumulator)
#else
        public unsafe Stats AccumulateTotalStats(Character character, Stats unsafeStatsAccumulator)
#endif
        {
            Item item = Item;
            if ((object)cachedTotalStats != null && item.LastChange <= cachedTime)
            {
                if ((object)unsafeStatsAccumulator != null)
                {
                    unsafeStatsAccumulator.AccumulateUnsafe(cachedTotalStats);
                }
                return cachedTotalStats;
            }
            if (item == null)
            {
                return null;
            }
            Item i1 = ItemMod1;
            Item i2 = ItemMod2;
            Item i3 = ItemMod3;
            Item i4 = ItemMod4;
            Item i5 = ItemMod5;
            bool volatileGem = false, volatileItem = false;
            bool itemMod1 = false;
            bool itemMod2 = false;
            bool itemMod3 = false;
            bool itemMod4 = false;
            bool itemMod5 = false;
            if (i1 != null && i1.MeetsRequirements(character, out volatileGem)) itemMod1 = true;
            volatileItem = volatileItem || volatileGem;
            if (i2 != null && i2.MeetsRequirements(character, out volatileGem)) itemMod2 = true;
            volatileItem = volatileItem || volatileGem;
            if (i3 != null && i3.MeetsRequirements(character, out volatileGem)) itemMod3 = true;
            volatileItem = volatileItem || volatileGem;
            if (i4 != null && i4.MeetsRequirements(character, out volatileGem)) itemMod4 = true;
            volatileItem = volatileItem || volatileGem;
            if (i5 != null && i5.MeetsRequirements(character, out volatileGem)) itemMod5 = true;
            volatileItem = volatileItem || volatileGem;
            if (volatileItem && unsafeStatsAccumulator != null)
            {
                unsafeStatsAccumulator.AccumulateUnsafe(item.Stats, true);
                if (RandomSuffixId != "0")
                {
                    //RandomSuffix.AccumulateStats(unsafeStatsAccumulator, item, RandomSuffixId);
                }
                if (itemMod1) unsafeStatsAccumulator.AccumulateUnsafe(i1.Stats, true);
                if (itemMod2) unsafeStatsAccumulator.AccumulateUnsafe(i2.Stats, true);
                if (itemMod3) unsafeStatsAccumulator.AccumulateUnsafe(i3.Stats, true);
                if (itemMod4) unsafeStatsAccumulator.AccumulateUnsafe(i4.Stats, true);
                if (itemMod5) unsafeStatsAccumulator.AccumulateUnsafe(i5.Stats, true);
                return null;
            }
            else
            {
                Stats totalItemStats = new Stats();
#if !SILVERLIGHT
                fixed (float* pRawAdditiveData = totalItemStats._rawAdditiveData, pRawMultiplicativeData = totalItemStats._rawMultiplicativeData, pRawNoStackData = totalItemStats._rawNoStackData)
                {
                    totalItemStats.BeginUnsafe(pRawAdditiveData, pRawMultiplicativeData, pRawNoStackData);
#endif
                    totalItemStats.AccumulateUnsafe(item.Stats, true);
                    if (RandomSuffixId != "0")
                    {
                        //RandomSuffix.AccumulateStats(totalItemStats, item, RandomSuffixId);
                    }
                    if (itemMod1) totalItemStats.AccumulateUnsafe(i1.Stats, true);
                    if (itemMod2) totalItemStats.AccumulateUnsafe(i2.Stats, true);
                    if (itemMod3) totalItemStats.AccumulateUnsafe(i3.Stats, true);
                    if (itemMod4) totalItemStats.AccumulateUnsafe(i4.Stats, true);
                    if (itemMod5) totalItemStats.AccumulateUnsafe(i5.Stats, true);
                    if (!volatileItem)
                    {
                        cachedTime = DateTime.Now;
                        cachedTotalStats = totalItemStats;
                        cachedTotalStats.GenerateSparseData();
                        if (unsafeStatsAccumulator != null)
                        {
                            unsafeStatsAccumulator.AccumulateUnsafe(cachedTotalStats);
                        }
                    }
#if !SILVERLIGHT

                    totalItemStats.EndUnsafe();
                }
#endif
                return totalItemStats;
            }
        }

        public static ItemInstance LoadFromId(string gemmedId)
        {
            if (string.IsNullOrEmpty(gemmedId))
                return null;
            return new ItemInstance(gemmedId);
        }

        #region IComparable<Item> Members
        public int CompareTo(ItemInstance other) { return ItemModdedId.CompareTo(other.ItemModdedId); }
        #endregion

        public bool FitsInSlot(CharacterSlot characterSlot)
        {
            return Item.FitsInSlot(characterSlot);
        }

        // helper functions to minimize fixing of models
        [XmlIgnore]
        public ItemSlot Slot
        {
            get
            {
                if (Item == null) return ItemSlot.None;
                return Item.Slot;
            }
        }

        [XmlIgnore]
        public ItemType Type
        {
            get
            {
                if (Item == null) return ItemType.None;
                return Item.Type;
            }
        }

        [XmlIgnore]
        public int MinDamage
        {
            get
            {
                if (Item == null) return 0;
                return Item.MinDamage;
            }
        }

        [XmlIgnore]
        public int MaxDamage
        {
            get
            {
                if (Item == null) return 0;
                return Item.MaxDamage;
            }
        }

        [XmlIgnore]
        public ItemDamageType DamageType
        {
            get
            {
                if (Item == null) return ItemDamageType.Kinetic;
                return Item.DamageType;
            }
        }

        public static bool operator ==(ItemInstance a, ItemInstance b)
        {
            if ((object)b == null || (object)a == null) return (object)a == (object)b;
            return a.ItemModdedId == b.ItemModdedId;
        }

        public static bool operator !=(ItemInstance a, ItemInstance b)
        {
            return !(a == b);
        }

        public override bool Equals(object obj)
        {
            ItemInstance o = obj as ItemInstance;
            if ((object)o != null)
            {
                return ItemModdedId == o.ItemModdedId;
            }
            return false;
        }

        public override int GetHashCode()
        {
            return ItemModdedId.GetHashCode();
        }

        public string Name 
        {
            get
            {
                if (Item == null) return null;
                return Item.Name;
            }
        }
    }
    #endregion

    [GenerateSerializer]
    public class ItemList : List<Item>
    {
        public ItemList() : base() { }
        public ItemList(IEnumerable<Item> collection) : base(collection) { }
    }

    [GenerateSerializer]
    public class ItemSet : List<ItemInstance>
    {
        public ItemSet() : base() { }
        public ItemSet(String name, IEnumerable<ItemInstance> collection) : base(collection) { Name = name; }
        private String _name = "Unnamed Set";
        public String Name { get { return _name; } set { _name = value; } }
        public string ToGemmedIDList() {
            List<string> list = new List<string>();
            foreach (ItemInstance ii in this) {
                if (ii == null) {
                    list.Add("");
                } else {
                    list.Add(ii.ItemModdedId);
                }
            }
            string retVal = "";
            int i = 0;
            foreach (string s in list) {
                retVal += string.Format("{0}:{1}|", (CharacterSlot)i, s);
                i++;
            }
            retVal = "\"" + Name.Replace("|","-") + "\"|" + retVal.Trim('|');
            return retVal;
        }
        public override string ToString()
        {
            string list = "";
            foreach (ItemInstance i in this) {
                list += string.Format("{0}, ", i);
            }
            list = list.Trim(',').Trim(' ');
            return Name + ": " + ListAsDesc;
        }
        public ItemInstance this[CharacterSlot cs] {
            get {
                if (this.Count <= 0) { return null; }
                if (this.Count < (int)cs + 1) { return null; }
                return this[(int)cs];
            }
            set { this[(int)cs] = value; }
        }
        public String ListAsDesc {
            get {
                string list = "";
                foreach (CharacterSlot cs in Character.EquippableCharacterSlots) {
                    if (this[cs] == null) { list += string.Format("{0}: {1}\r\n", cs.ToString(), "Empty"); }
                    else { list += string.Format("{0}: {1}\r\n", cs.ToString(), this[cs]); }
                }
                list = list.Trim('\r').Trim('\n');
                return list != "" ? list : "Empty List";
            }
        }
        public static ItemSet GenerateItemSetFromSavedString(string source) {
            ItemSet retVal = new ItemSet();
            //
            foreach (CharacterSlot cs in Character.EquippableCharacterSlots) { retVal.Add(null); }
            //
            List<String> sources = source.Split('|').ToList<String>();
            string name = sources[0].Replace("\"",""); // Read the Name
            sources.RemoveAt(0); // Pull the Name out of the list
            retVal.Name = name;
            foreach (String src in sources)
            {
                string[] srcs = src.Split(':');
                if (!String.IsNullOrEmpty(srcs[1])) {
                    retVal[(CharacterSlot)Enum.Parse(typeof(CharacterSlot), srcs[0], true)] = ItemInstance.LoadFromId(srcs[1]);
                } //else { retVal[(CharacterSlot)Enum.Parse(typeof(CharacterSlot), srcs[0], true)] = null; }
            }
            //
            return retVal;
        }
        /*public override bool Equals(object obj)
        {
            if (obj == null) { return false; } // fail on null object
            ItemSet other = (obj as ItemSet);
            if (other.Name != Name) { return false; } // fail on name mismatch
            if (other.Count != this.Count) { return false; } // fail on count mismatch
            foreach (CharacterSlot cs in Character.EquippableCharacterSlots) {
                if (this[cs] == null && other[cs] != null) {
                    return false; // fail on one slot being null and not the other
                } else if (this[cs] != null && other[cs] == null) {
                    return false; // fail on one slot being null and not the other
                } else if (this[cs] != other[cs]) {
                    return false; // fail on not matching in that slot
                }
            }
            return true;
            //return base.Equals(obj);
        }*/
    }

    [GenerateSerializer]
    public class ItemSetList : List<ItemSet>
    {
        public ItemSetList() : base() { }
        public ItemSetList(IEnumerable<ItemSet> collection) : base(collection) { }
    }
}
